package com.example.mywebrtc;

import android.text.TextUtils;
import android.util.Log;

import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.IceCandidate;
import org.webrtc.SessionDescription;

import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import io.socket.client.IO;
import io.socket.client.Socket;

public class SignalingClient {
    private final String TAG = "SignalingClient";
    private static SignalingClient instance;

    private SignalingClient() {
        init();
    }

    public static SignalingClient get() {
        if (instance == null) {
            synchronized (SignalingClient.class) {
                if (instance == null) {
                    instance = new SignalingClient();
                }
            }
        }
        return instance;
    }

    private Socket socket;
    private String room;
    private Callback callback;
    private ClientInitCallback clientInitCallback;
    //Android Https通讯 X509证书
    private final TrustManager[] trustAll = new TrustManager[]{
            new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {

                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            }
    };

    public void setCallback(Callback callback) {
        this.callback = callback;
    }

    public void setClientInitCallback(ClientInitCallback clientInitCallback) {
        this.clientInitCallback = clientInitCallback;
    }

    private void init() {
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustAll, null);
            IO.setDefaultHostnameVerifier(((hostname, session) -> true));
            IO.setDefaultSSLContext(sslContext);

            //Socket连接
            socket = IO.socket("https://192.168.0.109:8080");
            socket.connect();

            //新用户登录以及检测自身是否登录服务器成功
            socket.on("init", args -> {
                Log.i(TAG, "init:" + args[0] + ",my socket:" + socket.id());
                if (socket.id().equals(args[0])) {
                    if (clientInitCallback != null) {
                        clientInitCallback.initSocketSuccess();
                    }
                } else {
                    socket.emit("sendId",args[0]);
                    if (clientInitCallback != null) {
                        clientInitCallback.userJoined(String.valueOf(args[0]));
                    }
                }
            });

            //获取已在线用户
            socket.on("has_user", args -> {
                if (clientInitCallback != null) {
                    clientInitCallback.getUser(String.valueOf(args[0]));
                }
            });

            //接听电话
            socket.on("coming",args -> {
                if (clientInitCallback != null) {
                    clientInitCallback.comming((String) args[0]);
                }
            });
            //对方取消电话
            socket.on("comming_cancel", args -> {
                if (clientInitCallback != null) {
                    clientInitCallback.commingCancel((String) args[0]);
                }
            });
            //对方接听
            socket.on("comming_answered", args -> {
                if (clientInitCallback != null) {
                    clientInitCallback.commingAnswered((String) args[0]);
                }
            });

            //呼叫端房间已创建完成
            socket.on("call_room_create",args -> {
                if (clientInitCallback != null) {
                    clientInitCallback.callRoomCreate((String) args[0]);
                }
            });

            //接收命令，房间创建
            socket.on("created", args -> {
                Log.i(TAG, "room created");
                if (callback == null) {
                    Log.i(TAG, "callback is null");
                    return;
                }
                callback.onCreateRoom();
            });
            //接收命令，房间已满
            socket.on("full", args -> {
                if (callback == null) {
                    Log.i(TAG, "callback is null");
                    return;
                }
                Log.i(TAG, "room full");
            });
            //接收命令，加入房间
            socket.on("join", args -> {
                Log.i(TAG, "peer joined");
                if (callback == null) {
                    Log.i(TAG, "callback is null");
                    return;
                }
                callback.onPeerJoined(String.valueOf(args[1]));
            });
            //接收命令，自己加入房间
            socket.on("joined", args -> {
                Log.i(TAG, "self joined");
                if (callback == null) {
                    Log.i(TAG, "callback is null");
                    return;
                }
                callback.onSelfJoined();
            });
            //接收命令，log
            socket.on("log", args -> {
                Log.i(TAG, "log call " + Arrays.toString(args));
                if (callback == null) {
                    Log.i(TAG, "callback is null");
                    return;
                }
            });
            socket.on("disconnect",args -> {
                Log.i(TAG, "disconnect:" + args[0]);
                if (clientInitCallback != null) {
                    clientInitCallback.userLeave((String) args[0]);
                }
            });
            //接收命令，离开房间
            socket.on("bye", args -> {
                Log.i(TAG, "bye " + args[0]);
                if (callback == null) {
                    Log.i(TAG, "callback is null");
                    return;
                }
                if (args[0].equals(socket.id())) {
                    return;
                }
                callback.onPeerLeave((String) args[0]);

            });
            //接受命令，webRTC信令传输
            socket.on("message", args -> {
                Log.i(TAG, "message " + Arrays.toString(args));
                if (callback == null) {
                    Log.i(TAG, "callback is null");
                    return;
                }
                Object arg = args[0];
                if (arg instanceof String) {

                } else if (arg instanceof JSONObject) {
                    JSONObject data = (JSONObject) arg;
                    String type = data.optString("type");
                    if ("offer".equals(type)) {
                        callback.onOfferReceived(data);
                    } else if ("answer".equals(type)) {
                        callback.onAnswerReceived(data);
                    } else if ("candidate".equals(type)) {
                        callback.onIceCandidateReceived(data);
                    }
                }
            });
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }

    /**
     * 建立视频连接
     * @param roomName
     * @param toId
     */
    public void connection(String roomName, String toId) {
        if (TextUtils.isEmpty(roomName)) {
            roomName = socket.id();
        }
        room = roomName;
        //Socket发送了一条命令
        socket.emit("create or join", room);

        if (!TextUtils.isEmpty(toId)) {
            //向被呼叫端发送，房间已创建完成命令
            JSONObject jo = new JSONObject();
            try {
                jo.put("room",room);
                jo.put("to", toId);
                socket.emit("call_answered", jo);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }

    //传输信令
    public void sendIceCandidate(IceCandidate iceCandidate,String to) {
        JSONObject jo = new JSONObject();
        try {
            jo.put("type", "candidate");
            jo.put("label", iceCandidate.sdpMLineIndex);
            jo.put("id", iceCandidate.sdpMid);
            jo.put("candidate", iceCandidate.sdp);
            jo.put("from", socket.id());
            jo.put("to", to);

            socket.emit("message", jo);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    //传输sdp
    public void sendSessionDescription(SessionDescription sdp,String to) {
        JSONObject jo = new JSONObject();
        try {
            jo.put("type", sdp.type.canonicalForm());
            jo.put("sdp", sdp.description);
            jo.put("from", socket.id());
            jo.put("to", to);

            socket.emit("message", jo);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    /**
     * 拨打callId对应用户
     * @param callId
     */
    public void call(String callId) {
        socket.emit("call", callId);
    }

    /**
     * 取消正在拨打的电话
     * @param callId
     */
    public void callCancel(String callId) {
        socket.emit("call_cancel", callId);
    }

    //被呼叫方接受呼叫
    public void callOk(String callId) {
        socket.emit("call_take_in", callId);
    }

    /**
     * 离开房间
     */
    public void leaveRoom() {
        socket.emit("bye", room);
        room = "";
    }

    /**
     * 结束通话，断开连接
     */
    public void destroy() {
        socket.emit("bye", room);
        socket.disconnect();
        socket.close();
        instance = null;
    }

    public interface ClientInitCallback {
        void initSocketSuccess();

        void userJoined(String socketId);

        void userLeave(String socketId);

        void getUser(String id);

        void comming(String id);

        void commingCancel(String id);

        void commingAnswered(String id);

        void callRoomCreate(String room);
    }

    public interface Callback {
        void onCreateRoom();

        void onPeerJoined(String socketId);

        void onSelfJoined();

        void onPeerLeave(String msg);

        void onOfferReceived(JSONObject data);

        void onAnswerReceived(JSONObject data);

        void onIceCandidateReceived(JSONObject data);
    }
}
